/**
 * 
 */
package com.shy.base.dao.impl;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;
import javax.persistence.EntityManager;
import javax.persistence.Query;

import org.apache.commons.lang.StringUtils;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

import com.shy.base.dao.BaseDao;
import com.shy.base.vo.EntityTypeValue;
import com.shy.base.vo.QueryResult;

/**
 * <p>Title:BaseDaoImpl </p>
 * <p>Description: </p>
 * <p>Company:ipebg </p> 
 * @author H2013788
 * @date 2019年8月7日
 */
@Transactional 
@Repository
public class BaseDaoImpl<T> implements BaseDao<T>  {

	private Class<T> entityClazz;
	
	@Resource
 	protected EntityManager entityManager;
	
	@SuppressWarnings({ "unchecked", "rawtypes" })
	public BaseDaoImpl() {
		this.entityClazz = null;
        Class clazz = getClass();
        Type type = clazz.getGenericSuperclass();
        if (type instanceof ParameterizedType) {
            Type[] parameterizedType = ((ParameterizedType) type).getActualTypeArguments();
            this.entityClazz = (Class<T>) parameterizedType[0];
        }
	}

	public void clear(){
		entityManager.clear();
	}
	
	public T getById(Serializable id){
		return (T) entityManager.find(entityClazz,id);
	}

	@SuppressWarnings("unchecked")
	public List<T> findAll() {
		String jql = " FROM "+ entityClazz.getName();
		return entityManager.createQuery(jql).getResultList();
	}
	
	@SuppressWarnings("unchecked")
	@Override
	public List<T> findByIds(Serializable[] ids) {
		String jql = " FROM "+ entityClazz.getName() + " WHERE id in(:ids) ";
		return entityManager.createQuery(jql).setParameter("ids", Arrays.asList(ids)).getResultList();
	}
	

	public void delete(T entity) {
		entityManager.remove(entity);
	}

	public void deleteById(Serializable id) {
		entityManager.remove(entityManager.getReference(entityClazz,id));
	}
	
	public void deleteByIds(Serializable ... ids) {
		for(Serializable id : ids){
			deleteById(id);
		}
	}

	public void save(T entity) {
		entityManager.persist(entity);
	}
	
	public void update(T entity) {
		entityManager.merge(entity);
	}
	
	public QueryResult<T> getScrollData(Integer firstindex,Integer maxresult) {
		return getScrollData(firstindex,maxresult,null,null,null);
	}
	
	public QueryResult<T> getScrollData(Integer firstindex,Integer maxresult,LinkedHashMap<String,String> orderby) {
		return getScrollData(firstindex,maxresult,null,null,orderby);
	}
	
	
	@SuppressWarnings("unchecked")
	public QueryResult<T> getScrollData(Integer firstindex,Integer maxresult,
			String wherejql,Object[] queryParams,LinkedHashMap<String,String> orderby) {
		QueryResult<T> qr = new QueryResult<T>();
		String entityname = entityClazz.getName();
		String wherePartJql = "";
		if(StringUtils.isNotEmpty(wherejql)) wherePartJql = "WHERE " + wherejql;
		String selectJql = "SELECT o FROM "+ entityname+ " o "+ wherePartJql + buildOrderby(orderby);
		String countJql = "SELECT count(o.id) FROM "+ entityname+ " o "+ wherePartJql;
		Query query = entityManager.createQuery(selectJql);
		setQueryParams(query, queryParams);
		if(firstindex!=-1 && maxresult!=-1) query.setFirstResult(firstindex).setMaxResults(maxresult);
		qr.setResultlist(query.getResultList());
		query = entityManager.createQuery(countJql);
		setQueryParams(query, queryParams);
		qr.setTotalrecord((Long)query.getSingleResult());
		return qr;
	}
	
	
	@SuppressWarnings("unchecked")
    public QueryResult<T> getScrollData(Integer firstindex,Integer maxresult,
            String wherejql,Object[] queryParams,LinkedHashMap<String,String> orderby, QueryResult<T> qr) {
        if (qr == null ) {
            qr = new QueryResult<T>();
        }
        String entityname = entityClazz.getName();
        String wherePartJql = "";
        if(StringUtils.isNotEmpty(wherejql)) wherePartJql = "WHERE " + wherejql;
        String selectJql = "SELECT o FROM "+ entityname+ " o "+ wherePartJql + buildOrderby(orderby);
        String countJql = "SELECT count(o.id) FROM "+ entityname+ " o "+ wherePartJql;
        Query query = entityManager.createQuery(selectJql);
        setQueryParams(query, queryParams);
        if(firstindex!=-1 && maxresult!=-1) query.setFirstResult(firstindex).setMaxResults(maxresult);
        qr.setResultlist(query.getResultList());
        if (qr.getTotalrecord() == 0) {
            query = entityManager.createQuery(countJql);
            setQueryParams(query, queryParams);
            qr.setTotalrecord((Long)query.getSingleResult());
        }
        return qr;
    }
	
	
	
	@SuppressWarnings("unchecked")
	public QueryResult<T> getScrollDataBindName(Integer firstindex, Integer maxresult,
			String wherejql, List<EntityTypeValue> queryParams,
			LinkedHashMap<String, String> orderby) {
		QueryResult<T> qr = new QueryResult<T>();
		String entityname = entityClazz.getName();
		String wherePartJql = "";
		if(StringUtils.isNotEmpty(wherejql)) wherePartJql = "WHERE " + wherejql;
		String selectJql = "SELECT o FROM "+ entityname+ " o "+ wherePartJql + buildOrderby(orderby);
		String countJql = "SELECT count(o.id) FROM "+ entityname+ " o "+ wherePartJql;
		Query query = entityManager.createQuery(selectJql);
		setQueryParamsBindName(query, queryParams);
		if(firstindex!=-1 && maxresult!=-1) query.setFirstResult(firstindex).setMaxResults(maxresult);
		qr.setResultlist(query.getResultList());
		query = entityManager.createQuery(countJql);
		setQueryParamsBindName(query, queryParams);
		qr.setTotalrecord((Long)query.getSingleResult());
		return qr;
	}
	
	@SuppressWarnings("unchecked")
	public QueryResult<T> getScrollDataBindName(Integer firstindex, Integer maxresult,
			String wherejql, List<EntityTypeValue> queryParams,
			LinkedHashMap<String, String> orderby, String groupBy) {
		QueryResult<T> qr = new QueryResult<T>();
		String entityname = entityClazz.getName();
		String wherePartJql = "";
		if(StringUtils.isNotEmpty(wherejql)) wherePartJql = "WHERE " + wherejql;
		String selectJql = "SELECT o FROM "+ entityname+ " o "+ wherePartJql + (StringUtils.isNotEmpty(groupBy) ? " GROUP BY o." +  groupBy : "") + buildOrderby(orderby);
		String countJql = "SELECT count(o.id) FROM "+ entityname+ " o "+ wherePartJql;
		Query query = entityManager.createQuery(selectJql);
		setQueryParamsBindName(query, queryParams);
		if(firstindex!=-1 && maxresult!=-1) query.setFirstResult(firstindex).setMaxResults(maxresult);
		qr.setResultlist(query.getResultList());
		query = entityManager.createQuery(countJql);
		setQueryParamsBindName(query, queryParams);
		qr.setTotalrecord((Long)query.getSingleResult());
		
		return qr;
	}
	
	public long getCount() {
		String jql = "SELECT COUNT(id) FROM "+ entityClazz.getName() + " o";
		return (Long)entityManager.createQuery(jql).getSingleResult();
	}
	
	public void batchDelete(Collection<T> entitys) {
		int i=0;
		for(T entity:entitys){
			i++;
			entityManager.remove(entity);
			if( i % 20 ==0){
				entityManager.flush();
				entityManager.clear();
			}
		}
	}

	public void batchDeleteByIds(Collection<? extends Serializable> ids) {
		int i=0;
		for(Serializable id:ids){
			i++;
			T entity = entityManager.getReference(entityClazz,id);
			entityManager.remove(entity);
			if( i % 20 ==0){
				entityManager.flush();
				entityManager.clear();
			}
		}
	}

	public void batchSave(Collection<T> entitys) {
		int i = 0;
		for(T entity:entitys){
			i++;
			entityManager.persist(entity);
			if( i % 20 == 0){
				entityManager.flush();
				entityManager.clear();
			}
		}
	}

	public void batchUpdate(Collection<T> entitys) {
		int i=0;
		for(T entity:entitys){
			i++;
			entityManager.merge(entity);
			if( i % 20 ==0){
				entityManager.flush();
				entityManager.clear();
			}
		}
	}

	/**
	 * 设置参数
	 */
	protected void setQueryParams(Query query, Object[] queryParams){
		if(queryParams!=null && queryParams.length>0){
			for(int i=0; i<queryParams.length; i++){
				query.setParameter(i+1,queryParams[i]);
			}
		}
	}
	
	/**
	 * 按照名称绑定设置参数
	 */
	protected void setQueryParamsBindName(Query query, List<EntityTypeValue> queryParams){
		if(queryParams!=null && queryParams.size()>0){
			for(int i=0; i<queryParams.size(); i++){
				query.setParameter(queryParams.get(i).getParamName(), queryParams.get(i).getParamValue());
			}
		}
	}
	
	
	/**
	 * 组装order by语句
	 */
	protected String buildOrderby(LinkedHashMap<String,String> orderby){
		StringBuilder orderbyJql = new StringBuilder("");
		if(orderby!=null && orderby.size()>0){
			orderbyJql.append(" ORDER BY ");
			for(String key : orderby.keySet()){
				orderbyJql.append("o.").append(key).append(" ").append(orderby.get(key)).append(",");
			}
			orderbyJql.deleteCharAt(orderbyJql.length()-1);
		}
		return orderbyJql.toString();
	}


	@SuppressWarnings({ "rawtypes", "unchecked" })
	@Override
	public T getByProperty(String propertyName, Object value) {
		List list = findByProperty(propertyName, value);
		return list.isEmpty() ? null : (T)list.get(0);
	}
	
	
	@SuppressWarnings({ "rawtypes", "unchecked" })
	@Override
	public T getByProperty(Map<String, Object> propertyMap) {
		List list = findByProperty(propertyMap);
		return list.isEmpty() ? null : (T)list.get(0);
	}

	
	@Override
	public List<T> findByProperty(String propertyName, Object value) {
		return findByProperty(propertyName, value, null, null);
	}
	
	@SuppressWarnings("unchecked")
	@Override
	public List<T> findByProperty(String propertyName, Object value, String orderField, String orderDirect) {
		String selectJql = "SELECT o FROM "+ entityClazz.getName() + " o WHERE o." + propertyName + "=:param ";
		if(StringUtils.isNotEmpty(orderField)) {
			orderDirect = StringUtils.isNotEmpty(orderDirect) ? orderDirect : "desc";
			selectJql += " ORDER BY o." + orderField + " " + orderDirect;
		}
		Query query = entityManager.createQuery(selectJql);
		query.setParameter("param", value);
		return query.getResultList();
	}
	
	
    @SuppressWarnings("unchecked")
    @Override
    public List<T> findByProperty(String propertyName, Object value, String orderField, String orderDirect, int limit) {
        String selectJql = "SELECT o FROM "+ entityClazz.getName() + " o WHERE o." + propertyName + "=:param ";
        if(StringUtils.isNotEmpty(orderField)) {
            orderDirect = StringUtils.isNotEmpty(orderDirect) ? orderDirect : "desc";
            selectJql += " ORDER BY o." + orderField + " " + orderDirect;
        }
        Query query = entityManager.createQuery(selectJql);
        query.setParameter("param", value);
        query.setFirstResult(0);
        query.setMaxResults(limit);
        return query.getResultList();
    }
	
	

	@SuppressWarnings("unchecked")
	@Override
	public List<T> findByProperty(Map<String, Object> propertyMap) {
		if(propertyMap != null && !propertyMap.isEmpty()) {
			String hql = "SELECT o FROM "+ entityClazz.getName() + " o WHERE 1=1 ";
			for(String key : propertyMap.keySet()) {
                // 此处注意如果参数列表包含对象.属性这种  比如user.id 则参数名要把.去掉
                hql += " and o." + key + "=:" + key.replace(".", "");
            }
            Query query = entityManager.createQuery(hql);
            for(Map.Entry<String, Object> entry : propertyMap.entrySet()) {
                // 对应的将参数名中的.也去掉
                query.setParameter(entry.getKey().replace(".", ""), entry.getValue());
            }
			return query.getResultList();
		}
		return null;
	}
	
	
    @SuppressWarnings("unchecked")
    @Override
    public List<T> findByProperty(Map<String, Object> propertyMap, String orderField, String orderDirect, int limit) {
        if(propertyMap != null && !propertyMap.isEmpty()) {
            String hql = "SELECT o FROM "+ entityClazz.getName() + " o WHERE 1=1 ";
            for(String key : propertyMap.keySet()) {
                // 此处注意如果参数列表包含对象.属性这种  比如user.id 则参数名要把.去掉
                hql += " and o." + key + "=:" + key.replace(".", "");
            }
            if(StringUtils.isNotEmpty(orderField)) {
                orderDirect = StringUtils.isNotEmpty(orderDirect) ? orderDirect : "desc";
                hql += " ORDER BY o." + orderField + " " + orderDirect;
            }
            Query query = entityManager.createQuery(hql);
            for(Map.Entry<String, Object> entry : propertyMap.entrySet()) {
                // 对应的将参数名中的.也去掉
                query.setParameter(entry.getKey().replace(".", ""), entry.getValue());
            }
            query.setFirstResult(0);
            query.setMaxResults(limit);
            return query.getResultList();
        }
        return null;
    }
	
	
	@Override
	public T getUnique(String hql, Object... values) {
		List<T> list = findList(hql, values);
		return list == null || list.isEmpty() ? null : list.get(0);
	}
	
	
	/**
	 * 按HQL查询唯列表
	 * @param hql
	 * @param values 数量可变的参数,按顺序绑定.
	 * @return 对象
	 */
	@SuppressWarnings("unchecked")
	public List<T>  findList(final String hql, final Object... values) {
		return createQuery(hql, values).getResultList();
	}
	
	
	/**
	 * 根据查询HQL与参数列表创建Query对象.
	 * 与find()函数可进行更加灵活的操作.
	 * @param queryString
	 * @param values 命名参数,按名称绑定.
	 * @return Query
	 */
	public Query createQuery(final String queryString, final Object... values) {
		Query query = entityManager.createQuery(queryString);
		if (values != null) {
			for (int i = 1; i <= values.length; i++) {
				query.setParameter(i, values[i-1]);
			}
		}
		return query;
	}


}
